﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Runtime.Remoting.Messaging;

using Effects.Wave;
using Effects.GUI;
using Effects.Application;

namespace Effects.Application
{
    /// <summary>
    ///  Main class for the whole application.
    /// </summary>
    /// <remarks>
    ///   <para>
    ///     Main class of the whole application is <see cref="EffectsForm"/>
    ///     It consists of few important components: MainMenu, ShowWave panel, EffectBoard panel and Settings management. The main goal of 
    ///     <see cref="EffectsForm"/> class is to keep all components synchornized and provide functional connecions between them.  
    ///   </para>
    ///   <para>
    ///     Whole application works in two modes: Playing mode nad Recording mode. 
    ///   </para>
    ///   <para>
    ///     In Playing mode whole settings management system is not active so it ingores any changes that happen (unless an effect is removed or added).
    ///     In Recording mode settings management handles the changes in effects settings (values of parameter, ON/OFF switching of the effect)  and stores them.
    ///     These changes are used when playing the file in Recording mode and Settings management adjusts the effects parameters according to them.
    ///   </para>
    ///   <para></para>
    ///   <para>
    ///     Description of the most important components in <see cref="EffectsForm"/> class:  
    ///   </para>
    ///   <para>
    ///     Main menu - menu strip of the application. Allows user to open files, switches modes,... <see cref="EffectsForm"/> contains handlers for all menu items.
    ///   </para>
    ///   <para>
    ///     ShowPanel - <see cref="ShowPanel"/> It is a multifunction component, it displays waveform (and allows user to scroll and zoom it) and plays the wave file.
    ///     To control this playback it has buttons, that can be clicked by user. Application needs to react on this clicks and adjust the settings managemnt.
    ///     Therefore ShowWavePanel has methods that register handlers on the mentioned buttons.  
    ///   </para>
    ///   <para>
    ///     EffectBoard panel - <see cref="EffectBoardPanel"/> two panel (one for left and one for right channel). They store currently used effects and allow user mange them.
    ///     <see cref="EffectsForm"/> must provide conection between these boars and setting management system. 
    ///   </para>
    ///   <para>
    ///     Settings management - <see cref="SettingsManagement"/> it is system that "logs" changes in settings of the effects done by user and stores them to be later recreate them.
    ///     <see cref="EffectsForm"/> controls this system via binding it with Effect board panels and also via repeating commands from ShowWave panel buttons.
    ///   </para>
    ///   <para>
    ///     <see cref="EffectsForm"/> registers it's own handles to the buttons displayed by ShowWave panel. In this handler it repeats user's commands 
    ///     to the settings management system.
    ///   </para> 
    /// </remarks>
    public partial class EffectsForm : Form
    {
        #region costructor
        /// <summary>
        ///  Creates new instance of <see cref="EffectsForm"/>.
        /// </summary>
        public EffectsForm()
        {
            //AfterOpenInvoker = new MethodInvoker(AfterOpenOperations);
            InitializeComponent();
            InitializeOpenFileWorker();
            InitializeProcessWorker();
            InitializeSaveWorker();

            this.SetStyle(ControlStyles.AllPaintingInWmPaint |ControlStyles.UserPaint |ControlStyles.DoubleBuffer, true);
            this.BackEnableMultiThread = new BackEnableDelegate(this.BackEnable);
        }

        #endregion

        #region files

        /// <summary>
        ///  Currently opened wave file
        /// </summary>
        private Wave.Wave wav;


        /// <summary>
        ///  Path to currently opened file
        /// </summary>
        private string file;

        /// <summary>
        /// Path to currently opened settings file
        /// </summary>
        private string settingsFile;

        #endregion

        #region graphic_components
        // graphic components of EffectsForm

        /// <summary>
        ///  Instancte of <see cref="ShowPanel"/> that draws wave form, provides playback and buttons for playing and recording
        /// </summary>
        private ShowPanel show;


        /// <summary>
        ///  <see cref="EffectBoardPanel"/> for channel 1 (left channel).
        /// </summary>
        private EffectBoardPanel board1;

        /// <summary>
        ///  <see cref="EffectBoardPanel"/> for channel 2 (right channel).
        /// </summary>
        private EffectBoardPanel board2;

        /// <summary>
        ///  <see cref="EffectLibrary"/> for the whole application. Stores all availible effects.
        /// </summary>
        private EffectLibrary library;

        /// <summary>
        ///  Changes in effect settings management system.
        /// </summary>
        private SettingsManagement manager;

        #endregion

        #region menu_event_handlers
        // region for handling eventd from main menu

        #region menu_file_event_handlers
        // handling events form file submenu

        /// <summary>
        ///  File/Open item event handler.  Shows dialog for opening wave files.
        /// </summary>
        /// <param name="sender"> Object that has raised the event == File/Open menu item. </param>
        /// <param name="e"> Event arguments. </param>
        private void Open_Clicked(object sender, EventArgs e)
        {
            if (!OpennigFileRunning)
            {
                show.Stop();
                openWaveFileDialog.ShowDialog();
            }
            else
                MessageBox.Show("Another file currently being loaded.");
        }

        /// <summary>
        ///  File/Save item event handler. Saves currently opened file.
        /// </summary>
        /// <param name="sender"> Object that has raised the event == File/Save menu item. </param>
        /// <param name="e"> Event arguments. </param>
        private void Save_Clicked(object sender, EventArgs e)
        {
            show.Stop();
            
        }

        /// <summary>
        ///  File/SaveAs item event handler. Shows dialog for saving wave files.
        /// </summary>
        /// <param name="sender"> Object that has raised the event== File/Save as menu item. </param>
        /// <param name="e"> Event arguments. </param>
        private void SaveAs_Clicked(object sender, EventArgs e)
        {
            show.Stop();
            saveWaveFileDialog.ShowDialog();
        }

        /// <summary>
        ///  File/OpenSettingsFile item event handler. Shows dialog for opening settings files.
        /// </summary>
        /// <param name="sender"> Object that has raised the event == File/Open Settings file menu item. </param>
        /// <param name="e"> Event arguments. </param>
        private void OpenSettingsFile_Clicked(object sender, EventArgs e)
        {
            show.Stop();
            CheckSaved();
            openSettingsFileDialog.ShowDialog();
        }

        /// <summary>
        ///  File/SaveSettingsFile item event handler. Saves settings file.
        /// </summary>
        /// <param name="sender"> Object that has raised the event == File/Save Settings file menu item. </param>
        /// <param name="e"> Event arguments. </param>
        private void SaveSettingsFile_Clicked(object sender, EventArgs e)
        {
            show.Stop();
            if (settingsFile != null)
                SaveSettings(settingsFile);
            else
                saveSettingsFileDialog.ShowDialog();
        }

        /// <summary>
        ///  File/SaveSettingsFileAs event handler. Shows dialog for saving settings files.
        /// </summary>
        /// <param name="sender"> Object that has raised the event== File/Save Settings file as menu item. </param>
        /// <param name="e"> Event arguments. </param>
        private void SaveSettingsFileAs_Clicked(object sender, EventArgs e)
        {
            show.Stop();
            saveSettingsFileDialog.ShowDialog();
        }

        /// <summary>
        ///  File/Exit item event handler. Closes the whole application
        /// </summary>
        /// <param name="sender"> Object that has raised the event == File/Exit menu item. </param>
        /// <param name="e"> Event arguments </param>
        private void Exit_Clicked(object sender, EventArgs e)
        {
            Exit();
        }

        #endregion

        #region menu_effects_event_handlers
        // handling events from Effects submenu

        /// <summary>
        ///  Effects/Add item event handler. Show <see cref="EffectsForm.addEffectForm"/> dialog to the user. 
        /// </summary>
        /// <param name="sender"> Object that has raised the event == Effects/Add menu item. </param>
        /// <param name="e"> Event arguments. </param>
        private void Add_clicked(object sender, EventArgs e)
        {
            addEffectForm.ShowDialog();
        }

        /// <summary>
        ///  Effects/Process item event handler. Runs processing of the data.
        /// </summary>
        /// <param name="sender"> Object that has raised the event == Effects/Process menu item. </param>
        /// <param name="e"> Event arguments. </param>
        private void Process_Clicked(object sender, EventArgs e)
        {
            if (!processRunning)
            {
                Process();
            }
            //processWorker.RunWorkerAsync();
            //this.ProcessAsync();
            else
                MessageBox.Show("File is being currently processed");
        }

        /// <summary>
        ///  Effects/Back item event handler. Provides one step undo.
        /// </summary>
        /// <param name="sender"> Object that has raised the event == Effects/Back menu item. </param>
        /// <param name="e"> Event arguments. </param>
        private void Back_Clicked(object sender, EventArgs e)
        {
            this.Back();
        }


        #endregion

        #region menu_mode_event_handlers
        // handling events from Mode submenu

        /// <summary>
        ///  Mode/Playing item event handler. Switches mode to playing.
        /// </summary>
        /// <param name="sender"> Object that has raised the event == Mode/Play menu item.  </param>
        /// <param name="args"> Event arguments  </param>
        private void PlayModeClicked(object sender, EventArgs args)
        {
            manager.SetOff();
            this.show.RecordingMode = false;
            recording = false;

            board1.EnableEditChangesButton(false);
            board2.EnableEditChangesButton(false);
        }

        /// <summary>
        ///  Mode/Recording item event handler. Switches mode to recording.
        /// </summary>
        /// <param name="sender"> Object that has raised the event  == Mode/Recording menu item.  </param>
        /// <param name="args"> Event arguments  </param>
        private void RecordingModeClicked(object sender, EventArgs args)
        {
            //settingsManager.Mode = EffectSettingsManager.ManagingMode.Playing;
            manager.SetPlaying();
            this.show.RecordingMode = true;
            recording = true;

            board1.EnableEditChangesButton(true);
            board2.EnableEditChangesButton(true);
        }

        /// <summary>
        ///  Recording mode is on when true.
        /// </summary>
        private bool recording = false;

        #region later_changes_mode
        // region for later_changes_mode menu event handlers

        /// <summary>
        ///  Clear later changes On event handler. Turns this mode ON on <see cref="EffectsForm.manager"/>.
        /// </summary>
        /// <param name="sender"> Object that has raised the event == Mode/Clear later changes/ON menu item. </param>
        /// <param name="e"> Event arguments </param>
        private void LaterChangesON(object sender, EventArgs e)
        {
            manager.ClearLaterChanges(true);
            ToolStripMenuItem snd = (ToolStripMenuItem)sender;
            snd.Enabled = false;
            ((ToolStripMenuItem)((ToolStripMenuItem)MainMenu.Items[2]).DropDownItems[2]).DropDownItems[1].Enabled = true;
        }

        /// <summary>
        ///  Clear later changes Off event handler. Turns this mode OFF on <see cref="EffectsForm.manager"/>.
        /// </summary>
        /// <param name="sender"> Object that has raised the event == Mode/Clear later changes/OFF menu item. </param>
        /// <param name="e"> Event arguments </param>
        private void LaterChangesOFF(object sender, EventArgs e)
        {
            manager.ClearLaterChanges(false);
            ToolStripMenuItem snd = (ToolStripMenuItem)sender;
            snd.Enabled = false;
            ((ToolStripMenuItem)((ToolStripMenuItem)MainMenu.Items[2]).DropDownItems[2]).DropDownItems[0].Enabled = true;
        }

        #endregion

        #endregion

        #endregion

        #region open_wave_file
        // opening wave files stuff

        /// <summary>
        ///  BackroundWorker for opening wave files.
        /// </summary>
        private BackgroundWorker openFileWorker;

        /// <summary>
        ///  Indicates wheter there is currently running file loading operation.
        /// </summary>
        /// <remarks>
        ///  This field is used to prevent from opening two files simultaneously.
        /// </remarks>
        private bool OpennigFileRunning = false;

        /// <summary>
        ///  <see cref="EffectsForm.openWaveFileDialog"/> OpenFileDialog event handler. Starts asynchronous load of choosen file.
        /// </summary>
        /// <param name="sender"> Evetn sender == <see cref="EffectsForm.openWaveFileDialog"/>. </param>
        /// <param name="e"> Event arguments. </param>
        private void OpenWaveFileDialogOK(object sender, System.ComponentModel.CancelEventArgs e)
        {
            openFileWorker.RunWorkerAsync(openWaveFileDialog.FileName);

            showProgress.Text = "Loading file: " + openWaveFileDialog.FileName;
            showProgress.Progress = 0;
            showProgress.CancelEnabled = false;
            
            showProgress.ShowDialog();
        }

        /// <summary>
        ///  DoWork method for <see cref="EffectsForm.openFileWorker"/>. Loads file and returns info whether loading has been successful.
        /// </summary>
        /// <param name="sender"> Event sender == <see cref="EffectsForm.openFileWorker"/> </param>
        /// <param name="e"> Event arguments </param>
        private void OpenFile_DoWork(object sender, DoWorkEventArgs e)
        {
            string fileName = (string)e.Argument;
            e.Result = OpenFile(fileName);
        }

        /// <summary>
        ///  RunWorkerCompleted method for <see cref="EffectsForm.openFileWorker"/>. Test the result of <see cref="EffectsForm.OpenFile_DoWork"/> method 
        ///  and prepares application to work with the loaded file.
        /// </summary>
        /// <param name="sender"> Event sender == <see cref="EffectsForm.openFileWorker"/> </param>
        /// <param name="e"> Event arguments. </param>
        private void OpenFile_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            bool succes = (bool)e.Result;
            OpennigFileRunning = false;

            if (succes)
            {
                    // sets sample rate
                    this.board1.Board.SetSampleRate(wav.FmtChunk.SampleRate);
                    this.board2.Board.SetSampleRate(wav.FmtChunk.SampleRate);

                    // loading showwave
                    this.show.Wav = this.wav;
                    manager.Player = show.Player;

                    if (!SampleRate_ChannelsEqual())
                        manager.Convert(Convert.ToInt32(wav.FmtChunk.SampleRate), wav.FmtChunk.Channels);
                    AfterOpenOperations();
            }
        }

        /// <summary>
        ///  Opens file from given path and updates <see cref="EffectsForm.wav"/> field.
        /// </summary>
        /// <param name="fileName"> Path to the file.</param>
        /// <returns> True if opening was succesful, false otherwise </returns>
        private bool OpenFile(string fileName)
        {
            try
            {
                OpennigFileRunning = true;
                // opening file
                WaveReader reader = new WaveReader(fileName);
                reader.ReportProgress += this.OpenProgress;

                Wave.Wave result = reader.Load();
                if (result != null)
                    this.wav = result;
                else
                    throw new Exception();


                showProgress.Progress = 100;

                // changing title
                this.file = fileName;
                return true;
            }
            catch (InvalidFileFormatException e)
            {
                MessageBox.Show(e.Message);
                showProgress.CloseWithoutAsking();
                return false;
            }
            catch (System.IO.IOException e)
            {
                MessageBox.Show(e.Message);
                showProgress.CloseWithoutAsking();
                return false;
            }
            catch (Exception)
            {
                MessageBox.Show("Couldn't open file");
                showProgress.CloseWithoutAsking();
                return false;
            }

        }

        /// <summary>
        ///  Sets progress reported from WaveReader to the ProgressForm.
        /// </summary>
        /// <param name="sender"> Object that caused the event. </param>
        /// <param name="args"> Event arguments </param>
        private void OpenProgress(object sender, Wave.IoOperationProgressEventArgs args)
        {
            showProgress.Progress = args.Progress;
        }

        /// <summary>
        ///  Does miscellaneous small operations that are necessary after loading wave file. (change title of the form,...)
        /// </summary>
        private void AfterOpenOperations()
        {
            // change tittle
            this.Text = "Effect Box - " + System.IO.Path.GetFileName(this.file);

            waveFileModified = false;
            this.backWav = null;
            BackEnable(false);

            // enabling Save, Save As
            ToolStripMenuItem File = (ToolStripMenuItem)this.MainMenu.Items[0];
            File.DropDownItems[1].Enabled = true;
            File.DropDownItems[2].Enabled = true;
        }

        #endregion

        #region save_wave_file
        // saving wave files stuff

        /// <summary>
        ///  Prepares displaying progress and saves (asynchronously) currently opened wav to the file at given path.
        /// </summary>
        /// <param name="fileName"> Path to the output file. </param>
        private void SaveWaveFile(string fileName)
        {
            showProgress.Text = "Saving file: " + saveWaveFileDialog.FileName;
            showProgress.Progress = 0;
            showProgress.CancelEnabled = false;
            saveWorker.RunWorkerAsync(fileName);
            showProgress.ShowDialog();
        }

        /// <summary>
        ///  Inidicates whether wave file has been modified since last save.
        /// </summary>
        private bool waveFileModified;

        /// <summary>
        ///  BackgroundWorker for saving wave files.
        /// </summary>
        private BackgroundWorker saveWorker;

        /// <summary>
        ///  <see cref="EffectsForm.saveWaveFileDialog"/> SaveFileDialog event handler. Starts asynchronous load of choosen file.
        /// </summary>
        /// <param name="sender"> Event sender ==  <see cref="EffectsForm.saveWaveFileDialog"/> </param>
        /// <param name="e"> Event arguments. </param>
        private void SaveWaveFileDialogOK(object sender, System.ComponentModel.CancelEventArgs e)
        {
            SaveWaveFile(openSettingsFileDialog.FileName);
        }

        // DoWork method of saveWorker
        /// <summary>
        ///  DoWork method for <see cref="EffectsForm.saveWorker"/> BackgroundWorker. Saves file and returns the result of the <see cref="EffectsForm.Save"/> method.
        /// </summary>
        /// <param name="sender"> Event sender == <see cref="EffectsForm.saveWorker"/> </param>
        /// <param name="e"> Event arguments. </param>
        private void SaveWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            string fileName = (string)e.Argument;
            e.Result = Save(fileName);
        }

        /// <summary>
        ///  RunWorkerCompleted method for <see cref="EffectsForm.saveWorker"/> BackgroundWorker shows result to the user.
        /// </summary>
        /// <param name="sender"> Event sender == <see cref="EffectsForm.saveWorker"/> </param>
        /// <param name="e"> Event arguments. </param>
        private void SaveWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            bool succes = (bool)e.Result;
            if (succes)
            {
                showProgress.CloseWithoutAsking();

                // changing title
                file = saveWaveFileDialog.FileName;
                Text = "Effect Box - " + System.IO.Path.GetFileName(this.file);
                waveFileModified = false;
            }
        }

        /// <summary>
        ///   Writes <see cref="EffectsForm.wav"/> to the given file.
        /// </summary>
        /// <param name="fileName"> Path to the file where <see cref="EffectsForm.wav"/> will be written </param>
        /// <returns> Message describing result of the operation </returns>
        private bool Save(string fileName)
        {
            try
            {
                WaveWriter writer = new WaveWriter(fileName);
                writer.ReportProgress += this.SaveProgress;
                writer.Wave = this.wav;
                writer.Write();
                return true;
            }
            catch (Exception e)
            {
                MessageBox.Show(e.Message);
                showProgress.CloseWithoutAsking();
                return false;
            }

        }

        /// <summary>
        ///  Sets progress reported from WaveWriter to the ProgressForm.
        /// </summary>
        /// <param name="sender"> Object that has raised the event. </param>
        /// <param name="args"> Event arguments </param>
        private void SaveProgress(object sender, IoOperationProgressEventArgs args)
        {
            showProgress.Progress = args.Progress;
        }

        #endregion

        #region open_settings_file
        // opening settings files stuff

        /// <summary>
        ///  <see cref="EffectsForm.openSettingsFileDialog"/> OpenFileDialog event handler. Opens choosen settings file.
        /// </summary>
        /// <param name="sender"> Obejct that has raised the event == <see cref="EffectsForm.openSettingsFileDialog"/> </param>
        /// <param name="e"> Event arguments. </param>
        private void OpenSettingsFileDialog_OK(object sender, System.ComponentModel.CancelEventArgs e)
        {
            try
            {
                // create new serializer from choosen file
                SettingsSerializer serializer = new SettingsSerializer(openSettingsFileDialog.FileName);

                // deserialzie new manager, set path and Initialize the manager
                SettingsManagement newManager = serializer.Deserialize();
                newManager.Initialize(board1.Board, board2.Board, library);

                // kill old manager
                if (manager != null)
                    manager.Die();

                // assign new
                manager = newManager;
                settingsFile = openSettingsFileDialog.FileName;

                manager.Stop();
                if (show.Player != null)
                    manager.Player = show.Player;

                if (!SampleRate_ChannelsEqual())
                    manager.Convert(Convert.ToInt32(wav.FmtChunk.SampleRate), wav.FmtChunk.Channels);
            }
            catch (GUI.EffectNotFoundException ex)
            {
                MessageBox.Show("Unable the load settings file. " + ex.EffectName + " can't be found.");
            }
            catch (Exception)
            {
                MessageBox.Show("Error occured while loading the file. Please make sure you choose only supported *.esf file");
            }
        }


        #endregion

        #region save_settings_file
        // saving settings files stuff

        /// <summary>
        ///  <see cref="EffectsForm.saveSettingsFileDialog"/> SaveFileDialog event handler. Saves settings to the choosen file.
        /// </summary>
        /// <param name="sender"> Event sender == <see cref="EffectsForm.saveSettingsFileDialog"/>. </param>
        /// <param name="e"> Event arguments. </param>
        private void SaveSettingsFileDialog_OK(object sender, System.ComponentModel.CancelEventArgs e)
        {
            SaveSettings(saveSettingsFileDialog.FileName);
            settingsFile = openSettingsFileDialog.FileName;
        }

        /// <summary>
        ///  Saves settings to the file at given path.
        /// </summary>
        /// <param name="path"> Path to the file </param>
        private void SaveSettings(string path)
        {
            SettingsSerializer serializer = new SettingsSerializer(path);
            serializer.Serialize(manager);
        }

        #endregion

        #region exit
        // closing the application

        /// <summary>
        ///  Checks whether files are saved. If not asks user whether to save the files and saves them.
        /// </summary>
        private void CheckSaved()
        {
            if (waveFileModified)
            {
                DialogResult answer = MessageBox.Show("Wave file has not been saved. Do you want to save it now?", "File not saved", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
                if (answer == DialogResult.Yes)
                    SaveWaveFile(file);
            }
        }

        /// <summary>
        ///  Indicates closing the application. True - closing, false - not Closing.
        /// </summary>
        private bool exit=false;

        /// <summary>
        ///  Closes the application. Before that check whether are file saved.
        /// </summary>
        private void Exit()
        {
            CheckSaved();
            exit = true;
            Close();
        }

        #endregion

        #region process
        // processing the wave file and applying effects

        /// <summary>
        ///  BackgroundWorker for process operation. 
        /// </summary>
        BackgroundWorker processWorker;

        /// <summary>
        ///  Form for displaying the progress of various operations. It is used when opening/saving wave files and  processing the wave file.
        /// </summary>
        ProgressForm showProgress = new ProgressForm();

        /// <summary>
        ///  Indicates wheter there is currently running process operation.
        /// </summary>
        /// <remarks>
        ///  This field is used to prevent from running two process operations simultaneously.
        /// </remarks>
        private bool processRunning = false;

        /// <summary>
        ///  DoWork method for <see cref="EffectsForm.processWorker"/>. Process data.
        /// </summary>
        /// <param name="sender"> Event sender == <see cref="EffectsForm.processWorker"/> </param>
        /// <param name="e"> Event arguments. </param>
        private void processWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            EnableBoards(false);
            processRunning = true;

            e.Result = null;

            //ProgressForm showProgress = new ProgressForm();
            //showProgress.CancelProcess += new EventHandler(this.ProcessCancel);

            if (!object.ReferenceEquals(wav, null))
            {
                //creates new instace of Wave
                Wave.Wave output = new Wave.Wave(new DataChunk(0), wav.FmtChunk, 0);

                int frameSize = 128;
                double[] buffer = new double[frameSize];
                int counter;
                int positionInFile;
                int positionInChannel;

                // precompute progress-percentage per one channel.
                double perChannelPercentage = 100 / wav.FmtChunk.Channels;
                int channelLength = (int) wav.SampleLength / wav.FmtChunk.Channels;

                // process data
                for (int channel = 0; channel < wav.FmtChunk.Channels; channel++)
                { 
                    if(recording)
                        manager.Stop();
                    counter = 0;
                    positionInFile = 0;
                    positionInChannel = 0;

                    foreach (double sample in wav.GetSampleDoubleEnumerator(channel))
                    {
                        buffer[counter] = sample;
                        counter++;

                        if (counter == frameSize)
                        {
                            if (recording)
                            {
                                positionInFile += (channel + 1) * frameSize;
                                manager.ApplyChanges(positionInFile);
                            }

                            if (channel % 2 == 0)
                                buffer = board1.Board.Process(buffer);
                            else
                                buffer = board2.Board.Process(buffer);

                            output.ToAppend(buffer, channel);
                            buffer = new double[frameSize];
                            counter = 0;

                            positionInFile += (wav.FmtChunk.Channels - channel - 1) * frameSize;
                            positionInChannel += frameSize;

                            // set progress
                            showProgress.Progress = Convert.ToInt32(channel * perChannelPercentage + perChannelPercentage * (double) ((double)positionInChannel / (double) channelLength));

                            // check cancel
                            if (processWorker.CancellationPending)
                            {
                                e.Cancel = true;
                                e.Result = null;
                                return;
                            }
                        }
                    }

                    // now process reamining samples -  adding zeros, and processing
                    while (counter < frameSize)
                    {
                        buffer[counter] = 0;
                        counter++;
                    }

                    positionInFile += (channel + 1) * frameSize;
                    manager.ApplyChanges(positionInFile);

                    if (channel % 2 == 0)
                        buffer = board1.Board.Process(buffer);
                    else
                        buffer = board2.Board.Process(buffer);
                    output.ToAppend(buffer, channel);
                }

                if (processWorker.CancellationPending)
                {
                    e.Cancel = true;
                    e.Result = null;
                    return;
                }

                //append data
                output.Append();
                if (processWorker.CancellationPending)
                {
                    e.Cancel = true;
                    e.Result = null;
                    return;
                }

                showProgress.Progress = 100;

                e.Result = output;
            }
            else
               MessageBox.Show("No file opened");
        }

        /// <summary>
        ///  RunWorkerCompleted method for <see cref="EffectsForm.processWorker"/> BackgroundWorker. 
        /// </summary>
        /// <param name="sender"> Event sender == <see cref="EffectsForm.processWorker"/> </param>
        /// <param name="e"> Event arguments. </param>
        private void processWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            processRunning = false;
            EnableBoards(true);
            showProgress.Click -= this.ProcessCancel;

            if (e.Cancelled)
                return;
            
            Wave.Wave result = (Wave.Wave)e.Result;
            if (result != null)
            {

                // setting output as currently opened Wave
                wav = result;
                show.Wav = result;
                manager.SetPlayer(show.Player);


                waveFileModified = true;
                backWav = wav;
                BackEnable(true);
            }
        }

        /// <summary>
        ///  Processes <see cref="EffectsForm.wav"/> and applies the effects from boards.
        /// </summary>
        /// <returns> New instance of <see cref="Wave"/> that contains effected data. </returns>
        private void Process()
        {
            // test file opened;
            if (wav == null)
            {
                MessageBox.Show("No file opened");
                return;
            }

            showProgress.Text = "Process operation";
            showProgress.Progress = 0;
            showProgress.CancelEnabled = true;
            showProgress.CancelProcess += this.ProcessCancel;
            processWorker.RunWorkerAsync();
            showProgress.ShowDialog();
        }

        /// <summary>
        ///  Cancels process operation.
        /// </summary>
        /// <param name="sender"> Object that has raised the event. </param>
        /// <param name="args"> Event arguments. </param>
        private void ProcessCancel(object sender, EventArgs args)
        {
            processWorker.CancelAsync();
        }

        /// <summary>
        ///  Enables/disables menu item Effects/Process.
        /// </summary>
        /// <param name="enabled"> True- enables, false - disables. </param>
        private void ProcessEnable(bool enabled)
        {
            if (InvokeRequired)
            {
                BeginInvoke(new BackEnableDelegate(ProcessEnable), new object[] { enabled });
            }
            else
            {
                ToolStripMenuItem item = (ToolStripMenuItem)MainMenu.Items[1];
                item.DropDownItems[2].Enabled = enabled;
            }
        }

        #endregion

        #region initialize_background_workers
        // initializing all background workers

        /// <summary>
        ///  Initializes <see cref="EffectsForm.openFileWorker"/> BackgroundWorker.
        /// </summary>
        private void InitializeOpenFileWorker()
        {
            openFileWorker = new BackgroundWorker();
            openFileWorker.DoWork += new DoWorkEventHandler(OpenFile_DoWork);
            openFileWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(OpenFile_RunWorkerCompleted);
        }

        /// <summary>
        ///  Initializes <see cref="EffectsForm.saveWorker"/> BackgroundWorker.
        /// </summary>
        private void InitializeSaveWorker()
        {
            saveWorker = new BackgroundWorker();
            saveWorker.DoWork += new DoWorkEventHandler(SaveWorker_DoWork);
            saveWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(SaveWorker_RunWorkerCompleted);
        }

        /// <summary>
        ///  Initializes <see cref="EffectsForm.processWorker"/> BackgroundWorker.
        /// </summary>
        private void InitializeProcessWorker()
        {
            processWorker = new BackgroundWorker();
            processWorker.WorkerSupportsCancellation = true;
            processWorker.DoWork += new DoWorkEventHandler(processWorker_DoWork);
            processWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(processWorker_RunWorkerCompleted);
        }
        #endregion

        #region step_back
        // one step undo
        
        /// <summary>
        ///  Previous wave.
        /// </summary>
        private Wave.Wave backWav;

        /// <summary>
        ///  Brings back previous wave (undo).
        /// </summary>
        private void Back()
        {
            if (backWav != null)
            {
                wav = backWav;
                //player.Wave = wav;
                show.Wav = wav;
                backWav = null;
                BackEnable(false);
            }
        }

        /// <summary>
        ///  delegate for enabling Back menu item
        /// </summary>
        /// <param name="b"></param>
        private delegate void BackEnableDelegate(bool b);

        /// <summary>
        ///  Invoker for invoking <see cref="EffectsForm.BackEnable"/> method
        /// </summary>
        private BackEnableDelegate BackEnableMultiThread;

        // 
        /// <summary>
        ///  Enables or disables Effects/Back button in menu
        /// </summary>
        /// <param name="b"> Enable/disable parameter. </param>
        private void BackEnable(bool b)
        {
            if (InvokeRequired)
            {
                BeginInvoke(BackEnableMultiThread, new object[]{b});
            }
            else
            {
                ToolStripMenuItem Effects = (ToolStripMenuItem)MainMenu.Items[1];
                Effects.DropDownItems[1].Enabled = b;
            }
        }

        #endregion 

        #region enabling effect_boards
        // enabling effect boards

        /// <summary>
        ///  Delegate for enabling effect boards.
        /// </summary>
        /// <param name="b"></param>
        private delegate void EnableBoardsDelegate(bool b);

        /// <summary>
        ///  Enables both displayed EffectBoards (<see cref="EffectBoardPanel"/>).
        /// </summary>
        /// <param name="b"> Enabling parameter: true - enabled, false - diasbled. </param>
        private void EnableBoards(bool b)
        {
            if (InvokeRequired)
            {
                EnableBoardsDelegate del = new EnableBoardsDelegate(EnableBoards);
                BeginInvoke(del, new object[] { b });
            }
            else
            {
                this.board1.Enabled = b;
                this.board2.Enabled = b;
            }
        }

        #endregion

        #region show_panel_buttons_handler
        // event handlers for show panel button clicks

        /// <summary>
        ///  Event handler for PlayButton of <see cref="EffectsForm.show"/>
        /// </summary>
        /// <param name="sender"> Object that has raised the event </param>
        /// <param name="e"> Event arguments </param>
        /// <remarks>
        ///  <para>
        ///   This method is called when user clicks Play button on the <see cref="EffectsForm.show"/> ShowPanel. ShowPanel stars playing the file itself,
        ///   this handler must keep other components (such as <see cref="EffectsForm.manager"/>,...) aware of this action
        ///  </para>
        /// </remarks>
        private void ShowPlayClicked(object sender, EventArgs e)
        {
            ProcessEnable(false);
            BackEnable(false);

            if (recording)
            {
                manager.SetPlaying();
                manager.Play();
                EnableEffectsMenu(false);
                board1.EnableButtons(false);
                board2.EnableButtons(false);
            }
        }

        /// <summary>
        ///  Event handler for PauseButton of <see cref="EffectsForm.show"/>
        /// </summary>
        /// <param name="sender"> Object that has raised the event </param>
        /// <param name="e"> Event arguments </param>
        /// <remarks>
        ///  <para>
        ///   This method is called when user clicks Pause button on the <see cref="EffectsForm.show"/> ShowPanel. ShowPanel stars playing the file itself,
        ///   this handler must keep other components (such as <see cref="EffectsForm.manager"/>,...) aware of this action
        ///  </para>
        /// </remarks>
        private void ShowPauseClicked(object sender, EventArgs e)
        {
            if (recording)
                manager.Pause();
        }

        /// <summary>
        ///  Event handler for StopButton of <see cref="EffectsForm.show"/>
        /// </summary>
        /// <param name="sender"> Object that has raised the event </param>
        /// <param name="e"> Event arguments </param>
        /// <remarks>
        ///  <para>
        ///   This method is called when user clicks Stop button on the <see cref="EffectsForm.show"/> ShowPanel. ShowPanel stars playing the file itself,
        ///   this handler must keep other components (such as <see cref="EffectsForm.manager"/>,...) aware of this action
        ///  </para>
        /// </remarks>
        private void ShowStopClicked(object sender, EventArgs e)
        {
            ProcessEnable(true);
            if (backWav != null)
                BackEnable(true);

            if (recording)
            {
                manager.Stop();
                EnableEffectsMenu(true);
                board1.EnableButtons(true);
                board2.EnableButtons(true);
            }
        }

        /// <summary>
        ///  Event handler for RecordButton of <see cref="EffectsForm.show"/>
        /// </summary>
        /// <param name="sender"> Object that has raised the event </param>
        /// <param name="e"> Event arguments </param>
        /// <remarks>
        ///  <para>
        ///   This method is called when user clicks Record button on the <see cref="EffectsForm.show"/> ShowPanel. ShowPanel stars playing the file itself,
        ///   this handler must keep other components (such as <see cref="EffectsForm.manager"/>,...) aware of this action
        ///  </para>
        ///  <para>
        ///   Defensive approach is used in the method when testing <see cref="EffectsForm.recording"/> memeber. Call of this handler should never happen when application is in playing
        ///   mode, bacause in playing mode Record and RecordNew buttons are inaccessible for the user.
        ///  </para>
        /// </remarks>
        private void ShowRecordClicked(object sender, EventArgs e)
        {
            ProcessEnable(false);
            BackEnable(false);

            // defensive approach, when mode is playing Recod button should be inaccessible for the user
            if (recording)
            {
                manager.SetRecording();
                manager.Record();
                EnableEffectsMenu(false);
                board1.EnableButtons(false);
                board2.EnableButtons(false);
            }
        }

        /// <summary>
        ///  Event handler for RecordNewButton of <see cref="EffectsForm.show"/>
        /// </summary>
        /// <param name="sender"> Object that has raised the event </param>
        /// <param name="e"> Event arguments </param>
        /// <remarks>
        ///  <para>
        ///   This method is called when user clicks Record New button on the <see cref="EffectsForm.show"/> ShowPanel. ShowPanel stars playing the file itself,
        ///   this handler must keep other components (such as <see cref="EffectsForm.manager"/>,...) aware of this action
        ///  </para>
        ///  <para>
        ///   Defensive approach is used in the method when testing <see cref="EffectsForm.recording"/> memeber. Call of this handler should never happen when application is in playing
        ///   mode, bacause in playing mode Record and RecordNew buttons are inaccessible for the user.
        ///  </para>
        /// </remarks>
        private void ShowRecordNewClicked(object sender, EventArgs e)
        {
            ProcessEnable(false);
            BackEnable(false);

            if (this.recording)
            {
                manager.SetRecording();
                manager.RecordNew();

                EnableEffectsMenu(false);
                board1.EnableButtons(false);
                board2.EnableButtons(false);
            }
        }

        #endregion

        #region adding_effects
        // adding new effects

        /// <summary>
        ///  Allows user to choose effect to add.
        /// </summary>
        /// <remarks>
        ///  <para>
        ///   It is shown to the user to allow him choose effect that will be added to the one of the <see cref="EffectBoard"/>.
        ///   It raises event that specifies which type of effect is to be added. This event is handled by <see cref="EffectsForm.AddEffect"/> method
        ///  </para>
        /// </remarks>
        private AddEffectForm addEffectForm;

        /// <summary>
        ///  <see cref="EffectsForm.addEffectForm"/> event handler. Adds effect to appropriate EffectBoard
        /// </summary>
        /// <param name="sender"> Object that has raised the event. </param>
        /// <param name="e"> Event argument containing <see cref="global::Effects.Effects.Effect"/> to be added </param>
        private void AddEffect(object sender, AddEffectEventArgs e)
        {
            // add to board 1
            if(e.Channel==0)
                this.board1.AddEffect(e.AddedEffect);

            // add to board 2
            if(e.Channel==1)
                this.board2.AddEffect(e.AddedEffect);
        }

        #endregion

        #region loading_effects
        // loading effects from plugions

        /// <summary>
        ///  Loads effects from plugins
        /// </summary>
        /// <param name="showCurruptedFiles"> When true methods shows list of corrupted files to the user. </param>
        /// <returns> Array containing types of all loaded effects. </returns>
        /// <remarks>
        ///  <para>
        ///   Method looks for plugins in Effects directory, that is in the same directory as executed program.
        ///  </para>
        /// </remarks>
        private Type[] LoadEffects(bool showCurruptedFiles)
        {
            string path = System.IO.Path.GetDirectoryName(System.Windows.Forms.Application.ExecutablePath);
            EffectLoader load = new EffectLoader(path + "\\Effects");

            try
            {
                load.Load();

                // show corrupted files
                if (showCurruptedFiles)
                {
                    string[] corrupetdFiles = load.GetCorruptedFiles();
                    if (corrupetdFiles.Length > 0)
                    {
                        StringBuilder message = new StringBuilder("");
                        for (int i = 0; i < corrupetdFiles.Length; i++)
                            message.Append(corrupetdFiles[i]);

                        message.Remove(message.Length - 1, 1);
                        MessageBox.Show("These files are corrupted and therefore couldn't have been loaded correctly: \n" + message.ToString(), "Corrupted files");
                    }
                }

                return load.GetEffects();
            }
            catch (System.IO.DirectoryNotFoundException)   // directory for plugins not found, try to create it.    
            {
                try
                {
                    System.IO.Directory.CreateDirectory(path + "\\Effects");
                    return new Type[0];
                }
                catch (Exception)
                {
                    // something went wrong while creating directory, but we can do nothing.
                    return new Type[0];
                }
            }
            catch (Exception) // some other exception, just return empty array
            {
                return new Type[0];   
            }
        }
        #endregion

        #region testing_sampleRate_channels_equality
        // test manager and wav sample rate and channels count

        /// <summary>
        ///  Test wheter sampling rate and channels count of <see cref="EffectsForm.manager"/> nad <see cref="EffectsForm.wav"/> are same.
        /// </summary>
        /// <returns> True if they are or wav hasn't been opened yet, false othervise </returns>
        private bool SampleRate_ChannelsEqual()
        {
            if (this.wav == null)
                return true;
            if (this.manager == null)
                return true;

            return manager.SampleRate == wav.FmtChunk.SampleRate && manager.Channels == wav.FmtChunk.Channels;
        }

        #endregion
    }
}
